using System;
using System.Collections.Generic;

using Evaluant.OPath;

namespace Evaluant.Uss.ObjectContext.Navigator
{
    /// <summary>
    /// Compares two objects for equivalence, based on an opath expression
    /// </summary>
    public class NavigationComparer<T> : IComparer<T>
    {
        OPathQuery[] _Query;
        Navigator _Navigator;

        string[] sortAttributes;
        int[] sortDirection;

        private const string ASC = "asc";

        /// <summary>
        /// Creates a new <see cref="NavigationComparer"/> instance.
        /// </summary>
        /// <param name="path">Path following by asc or desc (asc if nothing). If more than one Path separate them with comma</param>
        public NavigationComparer(string path)
        {
            if (path == null)
                throw new ArgumentNullException("path");

            sortAttributes = path.Split(',');
            sortDirection = new int[sortAttributes.Length];
            _Query = new OPathQuery[sortAttributes.Length];

            for (int i = 0; i < sortAttributes.Length; i++)
            {
                string[] values = sortAttributes[i].Trim().Split();

                if (values.Length > 2)
                    throw new ArgumentException("path");

                sortAttributes[i] = values[0].Trim();
                string dir = (values.Length == 2) ? values[1].Trim().ToLower() : ASC;
                sortDirection[i] = dir == ASC ? 1 : -1;

                _Query[i] = new OPathQuery(String.Format("this.{0}", sortAttributes[i]));
                _Query[i].Compile();
            }

            _Navigator = new Navigator();
        }


        /// <summary>
        /// Performs a comparison of two objects of the same type and returns a value indicating whether one is less than, equal to or greater than the other.
        /// </summary>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns></returns>
        public int Compare(T x, T y)
        {
            int result = 0;
            int numAtt = 0;

            while (numAtt < _Query.Length)
            {
                _Navigator.Roots.Clear();
                _Navigator.Roots.Add(x);
                object xValue;
                object[] xValues = _Navigator.SelectElements(_Query[numAtt].Path);
                if (xValues.Length > 0)
                    xValue = xValues[0];
                else
                    xValue = null;

                _Navigator.Roots.Clear();
                _Navigator.Roots.Add(y);
                object yValue;
                object[] yValues = _Navigator.SelectElements(_Query[numAtt].Path);
                if (yValues.Length > 0)
                    yValue = yValues[0];
                else
                    yValue = null;

                if (xValue == null && yValue == null)
                    result = 0;

                if (xValue == null)
                    return -1 * sortDirection[numAtt];

                if (yValue == null)
                    return sortDirection[numAtt];

                result = sortDirection[numAtt] * Comparer<object>.Default.Compare(xValue, yValue);
                if (result != 0)
                    return result;

                numAtt++;
            }

            return 0;
        }
    }
}
